-
Notifications
You must be signed in to change notification settings - Fork 22.7k
Make the getMessage
function for the javascript example more robust.
#39672
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
The old getMessage function had an awful bug where if you made multiple unawaited calls to the function, it wouldn't behave correctly because previous reads had not yet completed, and all you were doing was reading 4 bytes at a time from the input into the header buffer. It also had an awful bug of not reading the correct amount of data, because the buffer given to the read function was not exactly sized for the expected message size, so it was possible that the amount of data that you got back was larger than the size that the header indicated if the extension had sent more than one message. The new getMessage function solves both of those issues. The first issue is solved by using a promise and a while loop to trap the execution of further calls at the top of the function while there is already a read in progress. Each call to getMessage is let through the trap one at a time as a read completes. If there are no pending reads, then a call to getMessage does not get trapped at the top of the function, because the read promise will be null, and so the loop will be skipped. The other issue is solved by using buffers which are appropriately sized according to the header information. I think this is also more succinct, since the old `readFullAsync` ultimately ended up doing that anyway at the end by passing the data array to the Uint8Array constructor. If you just create it from the header information and pass it directly to the read function, you can avoid dispatching to another function, and it prevents a bug.
files/en-us/mozilla/add-ons/webextensions/native_messaging/index.md
Outdated
Show resolved
Hide resolved
files/en-us/mozilla/add-ons/webextensions/native_messaging/index.md
Outdated
Show resolved
Hide resolved
Preview URLs (comment last updated: 2025-05-30 04:10:38) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please make sure your code follows our style guide. In particular:
- Do not use underscore-prefixed variables, and use camelCase.
- Names like
rs_rp
andrp
don't mean anything to me. Use full words that make sense. - There's no bug about "unawaited calls", because all calls are awaited in our example (we show you how
getMessage
should be used). If your calls togetMessage
are different then that's really a bug in your code. You seem to be implementing a lock with your_rp
but again the variables are poorly named and I can't read the code. I wouldn't recommend implementing a lock yourself though; it's too much mechanism for a trivial demo.
I understand the part about the size mismatch though. I don't know why the original version is using a loop instead of preallocating the whole thing.
Changes made by [mdn-linter] in PR mdn#39672. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Change the naming of variables to match the style on MDN. Try to make the code a little easier to understand.
@Josh-Cena Thank you for your feedback. I made some changes to it which I hope you'll accept. Totally understand if you don't want to put this on MDN, though. Let me know if you would accept it if the variables were named differently or something like that. Thanks. |
Thanks, this looks much better now. I made some tiny updates to your code, mainly wrapping it with |
@Josh-Cena Good idea on the try/finally |
files/en-us/mozilla/add-ons/webextensions/native_messaging/index.md
Outdated
Show resolved
Hide resolved
Co-authored-by: rebloor <git@sherpa.co.nz>
The `try` block is wrapping the calls to `input.read` because those calls can sometimes fail with an error. If they do, then we want to make sure that we still close the open file handle.
One more tiny change, which is just to close the file handle from within the finally block as well. |
Large messages from the extension may not be read all in one go, so you need to loop to get the entire message, and add the bytesRead from each read to an offset value which allows you to continue reading the next chunk of data into your buffer at that offset.
files/en-us/mozilla/add-ons/webextensions/native_messaging/index.md
Outdated
Show resolved
Hide resolved
…ex.md Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Oops, I need the loop to be able to get the entire message. But we can do better than the original code by keeping track of the offset into the message buffer where new data needs to be inserted, and by not reading more than the length indicated by the header by passing that offset to the read method along with the message buffer. I think... I think this is ready now. I'll grant you, the code became longer than I had hoped, but I think this finally solves all of the issues I had. 2 notes which you may want to add about the example:
I may eventually try to re-write this to work on both Linux and Windows using all of the knowledge I gained here, unless somebody beats me to it. |
The old getMessage function had a bug where if you made multiple unawaited calls to the function, it wouldn't behave correctly because previous reads had not yet completed, and all you were doing was reading 4 bytes at a time from the input into the header buffer. It also had a bug of not reading the correct amount of data, because the buffer given to the read function was not exactly sized for the expected message size, so it was possible that the amount of data that you got back was larger than the size that the header indicated if the extension had sent more than one message.
The new getMessage function solves both of those issues. The first issue is solved by using a promise and a while loop to trap the execution of extra calls at the top of the function while there is already a read in progress. Each call to getMessage is let through the trap one at a time as a read completes. If there are no pending reads, then a call to getMessage does not get trapped at the top of the function, because the read promise will be null, and so the loop will be skipped. The other issue is solved by using buffers which are appropriately sized according to the header information. I think this is also more succinct, since the old
readFullAsync
ultimately ended up doing that anyway at the end by passing the data array to the Uint8Array constructor. If you just create it from the header information and pass it directly to the read function, you can avoid dispatching to another function, and it prevents a bug.Description
Make the getMessage function better for the javascript example.
Motivation
The change will prevent the bugs I described, which makes it easier to develop a native messaging extension for the next person, I hope.
Additional details
https://nodejs.org/api/fs.html#filehandlereadbuffer-options
Related issues and pull requests